package com.common.service;

import cn.hutool.core.lang.Dict;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlHelper;
import com.common.entity.DataEntity;
import com.common.mapper.BaseMapper;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Service基类
 */
public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends DataEntity> implements BaseService<T> {

    /**
     * 持久层对象
     */
    @Autowired
    protected M baseMapper;

    /**
     * <p>
     * 判断数据库操作是否成功
     * </p>
     *
     * @param result 数据库操作返回影响条数
     * @return boolean
     */
    protected boolean retBool(Integer result) {
        return SqlHelper.retBool(result);
    }

    protected Class<T> currentModelClass() {
        return ReflectionKit.getSuperClassGenericType(getClass(), 1);
    }

    /**
     * <p>
     * 批量操作 SqlSession
     * </p>
     */
    protected SqlSession sqlSessionBatch() {
        return SqlHelper.sqlSessionBatch(currentModelClass());
    }

    /**
     * 释放sqlSession
     *
     * @param sqlSession session
     */
    protected void closeSqlSession(SqlSession sqlSession) {
        SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(currentModelClass()));
    }

    /**
     * 获取SqlStatement
     *
     * @param sqlMethod
     * @return
     */
    protected String sqlStatement(SqlMethod sqlMethod) {
        return SqlHelper.table(currentModelClass()).getSqlStatement(sqlMethod.getMethod());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean save(T entity) {
        return retBool(baseMapper.insert(entity));
    }

    /**
     * 批量插入
     *
     * @param entityList
     * @param batchSize
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveBatch(Collection<T> entityList, int batchSize) {
        int i = 0;
        String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
        try (SqlSession batchSqlSession = sqlSessionBatch()) {
            for (T anEntityList : entityList) {
                batchSqlSession.insert(sqlStatement, anEntityList);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }
        return true;
    }

    /**
     * <p>
     * TableId 注解存在更新记录，否插入一条记录
     * </p>
     *
     * @param entity 实体对象
     * @return boolean
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveOrUpdate(T entity) {
        if (null != entity) {
            if (entity.getIsNewRecord()) {
                return save(entity);
            } else {
                /*
                 * 更新成功直接返回，失败执行插入逻辑
                 */
                return updateById(entity) || save(entity);
            }
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList) {
        return saveOrUpdateBatch(entityList, 30);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
        if (CollectionUtils.isEmpty(entityList)) {
            throw new IllegalArgumentException("Error: entityList must not be empty");
        }
        int i = 0;
        try (SqlSession batchSqlSession = sqlSessionBatch()) {
            for (T anEntityList : entityList) {
                if (anEntityList.getIsNewRecord()) {
                    String sqlStatement = sqlStatement(SqlMethod.INSERT_ONE);
                    batchSqlSession.insert(sqlStatement, anEntityList);
                } else {
                    String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
                    MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                    param.put(Constants.ENTITY, anEntityList);
                    batchSqlSession.update(sqlStatement, param);
                    //不知道以后会不会有人说更新失败了还要执行插入 😂😂😂
                }
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
                batchSqlSession.flushStatements();
            }
        }
        return true;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteById(Serializable id) {
        return SqlHelper.delBool(baseMapper.deleteById(id));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteByMap(Map<String, Object> columnMap) {
        if (ObjectUtils.isEmpty(columnMap)) {
            throw ExceptionUtils.mpe("deleteByMap columnMap is empty.");
        }
        return SqlHelper.delBool(baseMapper.deleteByMap(columnMap));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean delete(T entity) {
        return SqlHelper.delBool(baseMapper.delete(entity));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean deleteByIds(Collection<? extends Serializable> idList) {
        return SqlHelper.delBool(baseMapper.deleteBatchIds(idList));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateById(T entity) {
        return retBool(baseMapper.updateById(entity));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean update(T entity) {
        return retBool(baseMapper.update(entity));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean updateBatchById(Collection<T> entityList, int batchSize) {
        if (CollectionUtils.isEmpty(entityList)) {
            throw new IllegalArgumentException("Error: entityList must not be empty");
        }
        int i = 0;
        String sqlStatement = sqlStatement(SqlMethod.UPDATE_BY_ID);
        try (SqlSession batchSqlSession = sqlSessionBatch()) {
            for (T anEntityList : entityList) {
                MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
                param.put(Constants.ENTITY, anEntityList);
                batchSqlSession.update(sqlStatement, param);
                if (i >= 1 && i % batchSize == 0) {
                    batchSqlSession.flushStatements();
                }
                i++;
            }
            batchSqlSession.flushStatements();
        }
        return true;
    }

    @Override
    public T getById(Serializable id) {
        return baseMapper.getById(id);
    }

    @Override
    public Collection<T> getListByIds(Collection<? extends Serializable> idList) {
        return baseMapper.getBatchIds(idList);
    }

    @Override
    public Collection<T> getListByMap(Map<String, Object> columnMap) {
        return baseMapper.getByMap(columnMap);
    }

    @Override
    public Map<String, Object> getMap(T entity) {
        return SqlHelper.getObject(baseMapper.getMaps(entity));
    }

    @Override
    public Object getObj(T entity) {
        return SqlHelper.getObject(baseMapper.getObjs(entity));
    }

    @Override
    public int getCount(T entity) {
        return SqlHelper.retCount(baseMapper.getCount(entity));
    }

    @Override
    public List<T> getList(T entity) {
        return baseMapper.getList(entity);
    }

    @Override
    public IPage<T> getPage(IPage<T> page, T query) {
        return baseMapper.getPage(page, query);
    }

    @Override
    public IPage<T> getPage(IPage<T> page, Dict query) {
        return baseMapper.getPage(page, query);
    }

    @Override
    public List<Map<String, Object>> getListMaps(T entity) {
        return baseMapper.getMaps(entity);
    }

    @Override
    public List<Object> getListObjs(T entity) {
        return baseMapper.getObjs(entity).stream().filter(Objects::nonNull).collect(Collectors.toList());
    }

    @Override
    public IPage<Map<String, Object>> getPageMaps(IPage<T> page, T query) {
        return baseMapper.getMapsPage(page, query);
    }

    @Override
    public IPage<Map<String, Object>> getPageMaps(IPage<T> page, Dict query) {
        return baseMapper.getMapsPage(page, query);
    }
}
